/*
 * Copyright (C) 2008+ Spark Zheng
 *
 * @file	src/random.c 
 * @brief       random module
 * @see		dlib
 * @module      dlib
 * @history 
 *      version 0.0.1   Spark Zheng init random module
 *      version 0.0.2   Spark Zheng add random-pickup/shuffle
 * TODO:
 *      -1. multi-random(Mean, Guass, Binary)
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

#include "lock.h"
#include "random.h"

static int rand_fd = 0;
static dlib_lock_t rand_lock;

inline int dlib_rand_init()
{
	if (rand_fd != 0)
		return -1;

	/* urandom not limited */
	if ((rand_fd = open("/dev/urandom", O_RDONLY)) < 0) {
		fprintf(stderr, "open /dev/urandom errror\n");
		return -2;
	}
	dlib_lock_init(&rand_lock);

	return 0;
}

inline void dlib_rand_exit()
{
	dlib_lock_lock(&rand_lock);
	if (rand_fd > 0)
		close(rand_fd);
	dlib_lock_unlock(&rand_lock);
	dlib_lock_exit(&rand_lock);

	return;
}

inline uint32_t dlib_rand32()
{
	uint32_t ret = 0;

	dlib_lock_lock(&rand_lock);
	if (rand_fd == 0 || lseek(rand_fd, 0, SEEK_SET) < 0 ||
	    read(rand_fd, &ret, sizeof (ret)) == 0) {
		srand(time(NULL));
		ret = (uint32_t) rand();
	}
	dlib_lock_unlock(&rand_lock);

	return ret;
}

inline uint64_t dlib_rand64()
{
	uint64_t ret = 0;

	dlib_lock_lock(&rand_lock);
	if (rand_fd == 0 || lseek(rand_fd, 0, SEEK_SET) < 0 ||
	    read(rand_fd, &ret, sizeof (ret)) == 0) {
		srand(time(NULL));
		ret = (uint64_t) rand();
	}
	dlib_lock_unlock(&rand_lock);

	return ret;
}

inline char *dlib_randstr(char *dist, uint32_t size)
{
	if (dist == NULL || size == 0 || size > 48)
		return NULL;

	uint32_t i;
	uint32_t ret = 0;
	const char table[63] =
	    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

	ret = dlib_rand32();
	for (i = 0; i < size; i++) {
		dist[i] = table[((ret >> i) + (i << 2)) % 62];
	}

	return dist;
}

/** the shuffer algorithm O(N) use random module */
void *dlib_random_shuffer(void *array, uint32_t len, uint32_t size)
{
	uint32_t i;
	uint8_t temp[size];

	if (array == NULL)
		return NULL;

	for (i = 0; i < len - 1; i++) {
		memmove(temp, array + i * size, size);
		memmove(array + i * size,
			array + (i + dlib_rand32() % (len - i)) * size, size);
		memmove(array + (i + dlib_rand32() % (len - i)) * size, temp,
			size);
	}

	return array;
}

/** pickup M elements from N elements(M <= N) O(N) use random module */
void *dlib_random_pickup(void *array, uint32_t array_len, void *pickup,
		    uint32_t pickup_len, uint32_t size)
{
	uint32_t i;
	uint32_t pickup_len_bak;
	uint32_t array_len_bak;

	if (array == NULL || pickup == NULL || size == 0)
		return NULL;

	pickup_len_bak = pickup_len;
	array_len_bak = array_len;
	for (i = 0; i < array_len; i++) {
		if (pickup_len_bak > 0 &&
		    dlib_rand32() % array_len_bak < pickup_len_bak) {
			memmove(pickup + (pickup_len - pickup_len_bak) * size,
				array + i * size, size);
			pickup_len_bak--;
		}
		array_len_bak--;
	}

	return pickup;
}
